﻿using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using EnetaMvc.ApplicationServices.Dto;
using EnetaMvc.Core.Domain.Common;
using EnetaMvc.Core.Domain.Events;
using EnetaMvc.Core.Domain.News;
using EnetaMvc.Core.Repositories;
using EnetaMvc.Infrastructure;
using EnetaMvc.Validation;
using StructureMap;

namespace EnetaMvc.ApplicationServices
{
    public class UserService : IUserService
    {
        private readonly IFileService _fileService;
        private readonly IUserRepository _userRepository;

        public UserService(IFileService fileService, IUserRepository userRepository)
        {
            _fileService = fileService;
            _userRepository = userRepository;
        }

        public bool UserTokenExists(string token)
        {
            var user = _userRepository.GetUserByLiveIdToken(token);
            return (user != null);
        }

        public bool IsUserNameFree(string username)
        {
            throw new NotImplementedException();
        }

        public MultiValidationResult RegisterUser(string username, string email, string token)
        {
            var user = new User();
            user.Email = email;
            user.LiveIdToken = token;
            user.UserName = username;
            user.Created = DateTime.Now;
            user.Modified = user.Created;

            var validator = ObjectFactory.GetInstance<UserValidator>();
            var results = validator.Validate(user);

            if (!results.IsValid())
                return results;

            var role = _userRepository.GetRoleByName("User");
            user.Roles.Add(role);

            _userRepository.SaveUser(user);

            return results;
        }


        public PagedResult<User> ListUsers(int page, int pageSize)
        {
            throw new NotImplementedException();
        }


        public UserDto GetUserById(long id)
        {
            var user = _userRepository.GetUserById(id);
            
            return GetUserDtoFromUser(user);
        }

        public UserDto GetUserByToken(string token)
        {
            var user = _userRepository.GetUserByLiveIdToken(token);

            return GetUserDtoFromUser(user);
        }

        public FullProfileDto GetFullProfileById(long id)
        {
            var user = _userRepository.GetUserById(id);
            if (user == null)
                return null;

            var profileDto = new FullProfileDto();

            Mapper.CreateMap<Blog, BlogDto>();
            Mapper.CreateMap<EventAttendee, EventAttendeeDto>();
            Mapper.CreateMap<MyRole, MyRoleDto>();
            Mapper.CreateMap<User, FullProfileDto>()
                  .ForMember(m => m.MyRoles, m => m.Ignore());             
            Mapper.Map(user, profileDto);
            
            foreach (var myRole in user.MyRoles)
            {
                var myRoleDto = new MyRoleDto();
                Mapper.Map(myRole, myRoleDto);
                profileDto.MyRoles.Add(myRoleDto);
            }

            return profileDto;
        }

        private UserDto GetUserDtoFromUser(User user)
        {
            var userDto = new UserDto();

            Mapper.CreateMap<User, UserDto>();
            Mapper.Map(user, userDto);

            return userDto;
        }


        public void SaveUser(FullProfileDto profile)
        {
            var user = _userRepository.GetUserById(profile.Id);

            Mapper.CreateMap<FullProfileDto, User>()
                  .ForMember(m => m.UserName, m => m.Ignore())
                  .ForMember(m => m.LiveIdToken, m => m.Ignore())
                  .ForMember(m => m.Id, m => m.Ignore())
                  .ForMember(m => m.Modified, m => m.Ignore())
                  .ForMember(m => m.Created, m => m.Ignore())
                  .ForMember(m => m.MyRoles, m => m.Ignore())
                  .ForMember(m => m.Locked, m => m.Ignore())
                  .ForMember(m => m.ModeratorOf, m => m.Ignore())
                  .ForMember(m => m.Blogs, m => m.Ignore());
            Mapper.Map(profile, user);

            foreach (var myRole in profile.MyRoles)
            {
                var checkRole = user.MyRoles.FirstOrDefault(r => r.Id == myRole.Id);
                if (checkRole == null)
                {
                    checkRole = _userRepository.GetMyRoleById(myRole.Id);
                    user.MyRoles.Add(checkRole);
                }
            }

            for (var i = user.MyRoles.Count - 1; i >= 0; i--)
            {
                var role = user.MyRoles[i];
                if (profile.MyRoles.FirstOrDefault(m => m.Id == role.Id) == null)
                    user.MyRoles.RemoveAt(i);
            }

            user.Modified = DateTime.Now;
            if (user.Id == 0)
                user.Created = user.Modified;

            _userRepository.SaveUser(user);
        }


        public IList<MyRoleDto> ListMyRoles()
        {
            var myRoles = _userRepository.ListMyRoles();
            var roleDtos = new List<MyRoleDto>(myRoles.Count);

            Mapper.CreateMap<MyRole, MyRoleDto>();
            Mapper.Map(myRoles, roleDtos);

            return roleDtos;
        }


        public IList<UserDto> ListLatelyJoinedUsers(int count)
        {
            var users = _userRepository.ListLatelyJoinedUsers(count);
            var userDtos = new List<UserDto>();

            Mapper.CreateMap<User, UserDto>();
            Mapper.Map(users, userDtos);

            return userDtos;
        }

        public void SaveProfileImage(long userId, byte[] imageBytes)
        {
            _fileService.SaveProfileImage(userId, imageBytes);
        }

        public void LockUser(long userId)
        {
            SetLockedState(userId, true);
        }

        public void UnlockUser(long userId)
        {
            SetLockedState(userId, false);
        }

        private void SetLockedState(long userId, bool lockedState)
        {
            var user = _userRepository.GetUserById(userId);
            if (user == null)
                return;

            user.Locked = lockedState;
            user.Modified = DateTime.Now;

            _userRepository.SaveUser(user);
        }

        public string[] GetUserRoles(long userId)
        {
            var user = _userRepository.GetUserById(userId);
            if (user == null)
                return null;

            return (from r in user.Roles
                    select r.Name).ToArray();
        }

        public string[] ListRoles()
        {
            return (from r in _userRepository.ListRoles()
                    select r.Name).ToArray();
        }

        public IList<UserDto> FindUsers(string searchString)
        {
            var users = _userRepository.FindUsers(searchString);
            var dtoList = new List<UserDto>();

            Mapper.CreateMap<User, UserDto>();
            Mapper.Map(users, dtoList);

            return dtoList;
        }


        public UserDto GetUserByUserName(string userName)
        {
            var user = _userRepository.GetUserByUserName(userName);
            if (user == null)
                return null;

            return GetUserDtoFromUser(user);
        }
    }
}
